package io.github.yedaxia.apidocs;

import com.alibaba.fastjson.JSONObject;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Utils {

    /**
     * object to pretty json
     *
     * @param map
     * @return
     */
    public static String toPrettyJson(Object map) {
        return JSONObject.toJSONString(map, true);
    }

    /**
     * object to simple json
     *
     * @param map
     * @return
     */
    public static String toJson(Object map) {
        return JSONObject.toJSONString(map);
    }

    /**
     * json string to object
     *
     * @param json
     * @param type
     * @param <T>
     * @return
     */
    public static <T> T jsonToObject(String json, Class<T> type) {
        return JSONObject.parseObject(json, type);
    }

    /**
     * write content to file
     *
     * @param f
     * @param content
     * @throws IOException
     */
    public static void writeToDisk(File f, String content) throws IOException {
        mkdirsForFile(f);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), "utf-8"));
        writer.write(content);
        writer.close();
    }

    /**
     * close stream
     *
     * @param stream
     */
    public static void closeSilently(Closeable stream) {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * simple read stream to String
     *
     * @param in
     * @return
     * @throws IOException
     */
    public static String streamToString(InputStream in) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
        char[] buffer = new char[4096];
        int bytesRead = -1;
        while ((bytesRead = reader.read(buffer)) != -1) {
            stringBuilder.append(buffer, 0, bytesRead);
        }
        reader.close();
        return stringBuilder.toString();
    }

    /**
     * judge input string is not empty
     *
     * @param str
     * @return
     */
    public static boolean isNotEmpty(String str) {
        return str != null && !str.isEmpty();
    }

    /**
     * some parse url may has double quotation, remove them
     *
     * @param rawUrl
     * @return
     */
    public static String removeQuotations(String rawUrl) {
        return rawUrl.replace("\"", "").trim();
    }

    /**
     * remove some characters like [* \n]
     *
     * @param content
     * @return
     */
    public static String cleanCommentContent(String content) {
        return content.replace("*", "").replace("\n", "").trim();
    }

    /**
     * get url with base url
     *
     * @param baseUrl
     * @param relativeUrl
     * @return
     */
    public static String getActionUrl(String baseUrl, String relativeUrl) {

        if (relativeUrl == null) {
            return "";
        }

        if (baseUrl == null) {
            return relativeUrl;
        }
        //当注解没写url时会默认取方法名，这里改掉。
        if ("".equals(relativeUrl)) {
            return baseUrl;
        }

        if (baseUrl.endsWith("/")) {
            baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
        }

        if (!relativeUrl.startsWith("/")) {
            relativeUrl = "/" + relativeUrl;
        }

        return baseUrl + relativeUrl;
    }

    /**
     * make first word lower case
     *
     * @param name
     * @return
     */
    public static String decapitalize(String name) {
        if (name != null && name.length() != 0) {
            if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))) {
                return name;
            } else {
                char[] chars = name.toCharArray();
                chars[0] = Character.toLowerCase(chars[0]);
                return new String(chars);
            }
        } else {
            return name;
        }
    }

    /**
     * make first word capitalize
     *
     * @param name
     * @return
     */
    public static String capitalize(String name) {
        if (name != null && name.length() != 0) {
            char[] chars = name.toCharArray();
            chars[0] = Character.toUpperCase(chars[0]);
            return new String(chars);
        } else {
            return name;
        }
    }

    /**
     * join string array , （ e.g. ([a,a,a] , .) = a.a.a )
     *
     * @param array
     * @param separator
     * @return
     */
    public static String joinArrayString(String[] array, String separator) {
        if (array == null || array.length == 0) {
            return "";
        }
        StringBuilder builder = new StringBuilder();
        for (int i = 0, len = array.length; i != len; i++) {
            builder.append(array[i]);
            if (i != len - 1) {
                builder.append(separator);
            }
        }
        return builder.toString();
    }

    /**
     * get file name without extension
     *
     * @param javaFile
     * @return
     */
    public static String getJavaFileName(File javaFile) {
        String fileName = javaFile.getName();
        return fileName.substring(0, fileName.lastIndexOf("."));
    }

    /**
     * search files match filter, store in result
     *
     * @param rootPath
     * @param filter
     * @param result
     * @param stopAtFirstResult stop when first file matches
     */
    public static void wideSearchFile(File rootPath, FilenameFilter filter, List<File> result, boolean stopAtFirstResult) {
        File[] fileList = rootPath.listFiles();
        List<File> dirPaths = new ArrayList<>();
        assert fileList != null;
        for (File f : fileList) {
            if (f.isFile() && filter.accept(f, f.getName())) {
                result.add(f);
                if (stopAtFirstResult) {
                    return;
                }
            } else if (f.isDirectory()) {
                dirPaths.add(f);
            }
        }

        for (File dir : dirPaths) {
            if (stopAtFirstResult && !result.isEmpty()) {
                return;
            }
            wideSearchFile(dir, filter, result, stopAtFirstResult);
        }
    }

    /**
     * judge dir is in file's path or not
     *
     * @param f
     * @param stopPath stopPath
     * @param dirName
     * @return
     */
    public static boolean hasDirInFile(File f, File stopPath, String dirName) {
        File p = f.getParentFile();
        while ((stopPath == null && p != null) || (stopPath != null && !p.getAbsolutePath().equals(stopPath.getAbsolutePath()))) {
            if (dirName.equals(p.getName())) {
                return true;
            }
            p = p.getParentFile();
        }
        return false;
    }

    /**
     * is value type or not
     *
     * @param value
     * @return
     */
    public static boolean isValueType(Object value) {
        return value instanceof Number
                || value instanceof String
                || value instanceof java.util.Date;
    }

    /**
     * get simple class name
     *
     * @param packageClass
     * @return
     */
    public static String getClassName(String packageClass) {
        String[] parts = packageClass.split("\\.");
        return parts[parts.length - 1];
    }

    /**
     * get project build tool type
     *
     * @param projectDir
     * @return
     */
    private static BuildToolType getProjectBuildTool(File projectDir) {
        if (new File(projectDir, "settings.gradle").exists()) {
            return BuildToolType.GRADLE;
        }

        if (new File(projectDir, "pom.xml").exists()) {
            return BuildToolType.MAVEN;
        }

        return BuildToolType.UNKOWN;
    }

    /**
     * get project modules name
     *
     * @return
     */
    public static List<String> getModuleNames() {
        BuildToolType buildToolType = BuildToolType.MAVEN;
        List<String> moduleNames = new ArrayList<>();
        // maven
        try {
            SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
            saxParser.parse(new File("E:\\workspace\\JApiDocs\\pom.xml"), new DefaultHandler() {
                String moduleName;
                boolean isModuleTag;

                @Override
                public void startElement(String uri, String localName, String qName, Attributes attributes) {
                    if ("module".equalsIgnoreCase(qName)) {
                        isModuleTag = true;
                    }
                }

                @Override
                public void endElement(String uri, String localName, String qName) {
                    if ("module".equalsIgnoreCase(qName)) {
                        moduleNames.add(moduleName);
                        isModuleTag = false;
                    }
                }

                @Override
                public void characters(char[] ch, int start, int length) {
                    if (isModuleTag) {
                        moduleName = new String(ch, start, length);
                    }
                }
            });
        } catch (Exception ex) {
            LogUtils.error("read pom.xml error", ex);
        }

        if (!moduleNames.isEmpty()) {
            LogUtils.info("find multi modules in this project: %s", Arrays.toString(moduleNames.toArray()));
        }

        return moduleNames;
    }

    /**
     * create dirs for file
     *
     * @param file
     */
    public static void mkdirsForFile(File file) {
        if (file.isFile() && !file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
    }
}
